type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';

type ScreenMap = Record<Breakpoint, boolean>;

type BreakpointMap = Partial<Record<Breakpoint, string>>;

type SubscribeFunc = (screens: ScreenMap) => void;
// addEventListener执行，整个项目所有subscribe的事件都会执行
//

// webpack-模块化开发-每个.ts文件就是一个闭包(()=> {})() ,闭包内定义了一个变量test111,如果没有export就无法使用
// const test111 = '1231';

// 从小到大顺序不能变，只取最后一个
export const responsiveMap: BreakpointMap = {
  xs: '(max-width: 575px)',
  sm: '(min-width: 576px)',
  md: '(min-width: 768px)',
  lg: '(min-width: 992px)',
  xl: '(min-width: 1200px)',
  xxl: '(min-width: 1600px)',
};

class ResponsiveObserve {
  constructor() {}

  private static subUid = 0;
  private static subscribers = new Map<number, SubscribeFunc>();

  private static screens: ScreenMap = {
    xxl: false,
    xl: false,
    lg: false,
    md: false,
    sm: false,
    xs: false,
  };

  private static matchHandlers: {
    [prop: string]: {
      mql: MediaQueryList;
      listener: (params: any) => void;
    };
  } = {};

  // 事件订阅
  static subscribe(func: SubscribeFunc) {
    if (!this.subscribers.size) {
      this.register();
      func(this.screens);
    }
    this.subUid += 1;
    this.subscribers.set(this.subUid, func);
    return this.subUid;
  }
  static unsubscribe(token: number) {
    this.subscribers.delete(token);
    if (!this.subscribers.size) this.unregister();
  }

  // 触发订阅的事件
  static dispatch(pointMap: ScreenMap) {
    this.screens = pointMap;
    // 循环的是value[]，初始化时subscribers = []
    // console.log('--dispatch--this.subscribers--:', this.subscribers);
    this.subscribers.forEach((func) => {
      // console.log('--初始化--dispatch--');
      func(pointMap);
    });
    return this.subscribers.size >= 1;
  }

  // 定义 何时触发订阅的事件, (如果没有unRegister,整个项目只会触发一次)
  static register() {
    (Object.keys(responsiveMap) as Breakpoint[]).forEach(
      (screen: Breakpoint) => {
        const matchMediaQuery = responsiveMap[screen]!;
        // console.log('--register--matchMediaQuery--:', matchMediaQuery);
        const mql = window.matchMedia(matchMediaQuery);
        const listener = ({
          matches,
          media,
        }: {
          matches: boolean;
          media: string;
        }) => {
          this.dispatch({
            ...this.screens,
            [screen]: matches,
          });
        };
        // addEventListener只有拖动视口时会执行，初始化不执行！
        mql.addEventListener<'change'>('change', listener);
        //初始化时,根据 mql的匹配结果执行dispatch
        listener(mql);
        this.matchHandlers[matchMediaQuery] = {
          mql,
          listener,
        };
      },
    );
  }

  static unregister() {
    (Object.keys(responsiveMap) as Breakpoint[]).forEach(
      (screen: Breakpoint) => {
        const matchMediaQuery = responsiveMap[screen]!;
        const handler = this.matchHandlers[matchMediaQuery];
        // handler?.mql.removeListener(handler?.listener);
        handler?.mql.removeEventListener<'change'>('change', handler?.listener);
      },
    );
    this.subscribers.clear();
    this.subUid = 0;
    this.screens = {
      xxl: false,
      xl: false,
      lg: false,
      md: false,
      sm: false,
      xs: false,
    };
  }
}

export default ResponsiveObserve;
